/**
 * watcher触发更新
 * Watcher有3种，渲染Watcher、计算Watcher、监听Watcher
 * 渲染Watcher和监听Watcher都是实例化一个Watcher，都会收集依赖，不过后者不是触发组件更新，而是触发监听的处理函数，并给其传入新旧值
 */
import { pushTarget, popTarget } from "./dep";
import { nextTick } from "../utils/nextTick";

let id = 0;

class Watcher {
  constructor(vm, exprOrfn, callback, options) {
    this.vm = vm;
    this.exprOrfn = exprOrfn;
    this.callback = callback;
    this.options = options;
    this.oldVal = null;
    this.newVal = null;
    this.value = null;
    this.lazy = options.lazy; // 如果watcher上的lazy为true，说明其是计算属性
    this.dirty = this.lazy; // dirty标识exprOrfn是否执行，比如取缓存则为false，就不执行
    this.id = id++;
    this.user = !!options.user; // true为监听watcher，false为渲染watcher
    this.deps = [];
    this.depsId = new Set();
    if (typeof exprOrfn === 'function') { // 针对渲染watcher和计算watcher
      this.getter = exprOrfn;
    } else { // string
      this.getter = function () { // 针对监听watcher，比如监听{a:{b:{c:1}}}里面的c，就得写'a.b.c'
        let path = exprOrfn.split('.');
        let val = vm;
        path.forEach(key => {
          val = val[key];
        })
        return val;
      }
    }
    // 若是计算属性，默认不立即执行exprOrfn
    this.value = this.lazy ? void 0 : this.get(); // 保存监听watcher监听到的初值
  }
  // 初次渲染
  get() {
    // 初次渲染之前，将该组件的watcher赋值给Dep.target
    pushTarget(this);
    // 调用getter方法，如果是渲染watcher就是初次渲染，如果是监听watcher就是获取初值
    const value = this.getter.call(this.vm); // 在执行getter方法时，就会访问状态数据，而访问数据就会触发Object.defineProperty的get，进行dep.depend()将watcher推入subs
    // 渲染完成之后，将Dep.target设置为null
    popTarget();
    return value;
  }
  run() {
    this.newVal = this.get();
    this.oldVal = this.value;
    this.value = this.newVal;
  }
  // 更新
  update() {
    // this.get();
    // set的时候，是会触发所有收集的依赖的，也即所有watcher的update都会执行
    if (this.lazy) {
      this.dirty = true;
    } else {
      // 不要数据更新后每次都调用(将this.get()注释，使用队列的方式queueWatcher)
      queueWatcher(this);
    }
  }
  // 存放dep
  addDep(dep) {
    let id = dep.id;
    if (!this.depsId.has(id)) {
      this.deps.push(dep);
      this.depsId.add(id);
      dep.addSub(this);
    }
  }
  // computed求值
  evaluate() {
    this.value = this.get();
    this.dirty = false;
  }
}

let queue = []; // 将需要批量更新的watcher存放到一个队列中
let has = {};
let pendding = false;

function queueWatcher(watcher) {
  let id = watcher.id; // 每个组件都是同一个 watcher，Watcher类只实例化过1次
  // 防抖，多次触发，只执行1次
  if (has[id] === undefined) {
    queue.push(watcher);
    has[id] = true;
    if (!pendding) {
      // 使用异步，待同步代码执行完毕，才会执行异步代码(这里是异步宏任务)
      // setTimeout(() => {
      //   flushWatcher();
      // })
      // 使用nextTick优化异步
      nextTick(flushWatcher);
    }
    pendding = true;
  }
}

function flushWatcher() {
  queue.forEach(w => {
    w.run();
    if (w.user) {
      // 触发callback回调(监听watcher)
      w.callback.call(w.vm, w.newVal, w.oldVal);
    } else if (!w.user) {
      // 触发callback回调(渲染watcher)
      w.callback();
    }
  });
  queue = [];
  has = {};
  pendding = false;
}

export default Watcher;